/* eslint-disable no-eval */
/* eslint-disable no-new-func */
// 常用的数据类型
const mapTag = '[object Map]'
const setTag = '[object Set]'
const arrayTag = '[object Array]'
const objectTag = '[object Object]'
const argsTag = '[object Arguments]'
const functionTag = '[object Function]'
const booleanTag = '[object Boolean]'
const stringTag = '[object String]'
const numberTag = '[object Number]'
const errorTag = '[object Error]'
const dateTag = '[object Date]'
const regexpTag = '[object RegExp]'
const symbolTag = '[object Symbol]'

// 可持续遍历的数据类型
const deepTag = [mapTag, setTag, arrayTag, objectTag, argsTag]

const reFlags = /\w*$/

function isObject (target) {
  const type = typeof target
  // return type === 'object' &&  type !== null
  return target !== null && (type === 'object' || type === 'function')
}

function getType (target) {
  // 使用 toString() 检测对象类型
  return Object.prototype.toString.call(target)
}

function getInit (target) {
  const Ctor = target.constructor
  return new Ctor()
}

function forEach (array, callback) {
  let index = -1
  const len = array.length
  while (++index < len) {
    callback(array[index], index)
  }
  return array
}

// 克隆正则表达
function cloneReg (target) {
  const result = new target.constructor(target.source, reFlags.exec(target))
  result.lastIndex = target.lastIndex
  return result
}

// 克隆symbol
function cloneSymbol (target) {
  return Object(Symbol.prototype.valueOf.call(target))
}

// 克隆函数
function cloneFunction (target) {
  const bodyReg = /(?<={)(.|\n)+(?=})/m
  const paramReg = /(?<=\().+(?=\)\s+{)/
  const funcString = target.toString()
  if (target.constructor) { // 普通函数
    const param = paramReg.exec(funcString)
    const body = bodyReg.exec(funcString)
    if (body) {
      // 匹配到函数体
      if (param) {
        // 匹配到函数参数
        const paramArr = param[0].split(',')
        return new Function(...paramArr, body[0])
      } else {
        return new Function(body[0])
      }
    } else {
      return null
    }
  } else {
    return eval(funcString) // 重新生成箭头函数
  }
}

function cloneOtherType (target, type) {
  console.log('cloneOtherType', { target, type })
  const Ctor = target.constructor
  switch (type) {
    case booleanTag:
    case numberTag:
    case stringTag:
    case errorTag:
    case dateTag:
      return new Ctor(target)
    case regexpTag:
      return cloneReg(target)
    case symbolTag:
      return cloneSymbol(target)
    case functionTag:
      return cloneFunction(target)
    default:
      break
  }
}

function clone (target, map = new WeakMap()) {
  // 对于非引用类型的数据可以直接返回
  if (!isObject(target)) return target

  const type = getType(target)
  let curTarget
  // 获取 target 初始化数据（如: {} / [] ）并赋值给 curTarget
  if (deepTag.includes(type)) {
    curTarget = getInit(target)
  } else {
    return cloneOtherType(target, type)
  }

  // 使用Map数据结构，查询当前target有没有被clone过，防止循环引用
  // Weak Map 是一种弱引用关系，当对象被设置为null后，会被垃圾回收机制执行并释放内存
  // 而 Map 是强引用关系，即使手动释放内存，垃圾回收机制也不会执行，内存无法释放
  if (map.get(target)) {
    return map.get(target)
  }
  map.set(target, curTarget)

  // 克隆 set
  if (type === setTag) {
    target.forEach(val => curTarget.add(clone(val, map)))
    return curTarget
  }

  // 克隆map
  if (type === mapTag) {
    target.forEach((val, key) => curTarget.set(key, clone(val, map)))
    return curTarget
  }

  // for 循环的效率比 while 低
  // for (const key in target) {
  //   curTarget[key] = clone(target[key], map)
  // }
  // 克隆对象和数组
  const isArray = Array.isArray(target)
  const keys = isArray ? undefined : Object.keys(target)
  forEach(keys || target, (value, key) => {
    if (keys) {
      key = value
    }
    curTarget[key] = clone(target[key], map)
  })
  return curTarget
}

const map = new Map()
map.set('name', 'husky')
map.set('age', '26')
const set = new Set()
set.add('set1')
set.add('set2')
const object = {
  str: 'str',
  num: 1,
  bool: false,
  undefined: undefined,
  null: null, // typeof 为 'object',
  arr: [1, 2, 3, 4],
  obj: { f: { f: { f: { f: { f: { f: 'f' } } } } } },
  map,
  set,
  symbol: Symbol(10),
  date: new Date(),
  reg: /^\d+$/,
  error: new Error('this is error'),
  func1: () => console.log('func1'),
  func2: function (a = 1, b = 2) {
    return a + b
  }

}
object.target = object // 循环引用 -> 由于递归进入死循环引导致栈内存溢出
const newObj = clone(object)
// const newObj2 = { ...object } // 浅拷贝，修改其内引用类型时，object指向的引用类型的值也会变
// newObj.c.test = 'test1'
// newObj.b[2] = 8
// newObj2.c.test = 'test2'
// newObj2.b[2] = 6
console.log({ newObj, object })

module.exports = clone
